// Copyright 2018 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.


`define REG_EVENT         6'b000000 //BASEADDR+0x00
`define REG_FC_MASK_0     6'b000001 //BASEADDR+0x04
`define REG_FC_MASK_1     6'b000010 //BASEADDR+0x08
`define REG_FC_MASK_2     6'b000011 //BASEADDR+0x0C
`define REG_FC_MASK_3     6'b000100 //BASEADDR+0x10
`define REG_FC_MASK_4     6'b000101 //BASEADDR+0x14
`define REG_FC_MASK_5     6'b000110 //BASEADDR+0x18
`define REG_FC_MASK_6     6'b000111 //BASEADDR+0x1C
`define REG_FC_MASK_7     6'b001000 //BASEADDR+0x20
`define REG_CL_MASK_0     6'b001001 //BASEADDR+0x24
`define REG_CL_MASK_1     6'b001010 //BASEADDR+0x28
`define REG_CL_MASK_2     6'b001011 //BASEADDR+0x2C
`define REG_CL_MASK_3     6'b001100 //BASEADDR+0x30
`define REG_CL_MASK_4     6'b001101 //BASEADDR+0x34
`define REG_CL_MASK_5     6'b001110 //BASEADDR+0x38
`define REG_CL_MASK_6     6'b001111 //BASEADDR+0x3C
`define REG_CL_MASK_7     6'b010000 //BASEADDR+0x40
`define REG_PR_MASK_0     6'b010001 //BASEADDR+0x44
`define REG_PR_MASK_1     6'b010010 //BASEADDR+0x48
`define REG_PR_MASK_2     6'b010011 //BASEADDR+0x4C
`define REG_PR_MASK_3     6'b010100 //BASEADDR+0x50
`define REG_PR_MASK_4     6'b010101 //BASEADDR+0x54
`define REG_PR_MASK_5     6'b010110 //BASEADDR+0x58
`define REG_PR_MASK_6     6'b010111 //BASEADDR+0x5C
`define REG_PR_MASK_7     6'b011000 //BASEADDR+0x60
`define REG_ERR_0         6'b011001 //BASEADDR+0x64
`define REG_ERR_1         6'b011010 //BASEADDR+0x68
`define REG_ERR_2         6'b011011 //BASEADDR+0x6C
`define REG_ERR_3         6'b011100 //BASEADDR+0x70
`define REG_ERR_4         6'b011101 //BASEADDR+0x74
`define REG_ERR_5         6'b011110 //BASEADDR+0x78
`define REG_ERR_6         6'b011111 //BASEADDR+0x7C
`define REG_ERR_7         6'b100000 //BASEADDR+0x80
`define REG_TIMER1_SEL_HI 6'b100001 //BASEADDR+0x84
`define REG_TIMER1_SEL_LO 6'b100010 //BASEADDR+0x88

module soc_event_generator #(
  parameter APB_ADDR_WIDTH = 12, //APB slaves are 4KB by default
  parameter PER_EVNT_NUM   = 17, // events coming from peripherals
  parameter APB_EVNT_NUM   = 16, // events generated by the cluster and going to the FC EU
  parameter EVNT_WIDTH     = 8,
  parameter FC_EVENT_POS   = 3
) (
  input  logic                      HCLK,
  input  logic                      HRESETn,
  input  logic [APB_ADDR_WIDTH-1:0] PADDR,
  input  logic [31:0]               PWDATA,
  input  logic                      PWRITE,
  input  logic                      PSEL,
  input  logic                      PENABLE,
  output logic [31:0]               PRDATA,
  output logic                      PREADY,
  output logic                      PSLVERR,
  input  logic                      low_speed_clk_i,
  input  logic [PER_EVNT_NUM-1:0]   per_events_i,    // events coming from peripherals
  output logic [1:0]                fc_events_o,     // events going to the FC EU
  output logic                      err_event_o,
  output logic                      timer_event_lo_o,
  output logic                      timer_event_hi_o,
  output logic                      fc_event_valid_o,
  output logic [EVNT_WIDTH-1:0]     fc_event_data_o,
  input  logic                      fc_event_ready_i,
  output logic                      cl_event_valid_o,
  output logic [EVNT_WIDTH-1:0]     cl_event_data_o,
  input  logic                      cl_event_ready_i,
  output logic                      pr_event_valid_o,
  output logic [EVNT_WIDTH-1:0]     pr_event_data_o,
  input  logic                      pr_event_ready_i
);

  genvar j;

  localparam EVNT_NUM = PER_EVNT_NUM + APB_EVNT_NUM + 1; //number of events going to the event BUS

  logic [7:0] r_timer_sel_hi;
  logic [7:0] r_timer_sel_lo;

  logic [EVNT_WIDTH-1:0] s_event_data ;
  logic                  s_event_valid;
  logic                  s_event_ready;

  logic [EVNT_NUM-1:0] s_err   ;
  logic  [      255:0] r_err   ;
  logic [EVNT_NUM-1:0] s_req   ;
  logic [EVNT_NUM-1:0] s_ack   ;
  logic [EVNT_NUM-1:0] s_events;
  logic [EVNT_NUM-1:0] s_grant ;

  logic [5:0] s_apb_addr;

  logic [255:0] r_fc_mask;
  logic [255:0] r_cl_mask;
  logic [255:0] r_pr_mask;

  logic [EVNT_NUM-1:0] s_fc_mask;
  logic [EVNT_NUM-1:0] s_cl_mask;
  logic [EVNT_NUM-1:0] s_pr_mask;

  logic s_ready_fc;
  logic s_ready_cl;
  logic s_ready_pr;

  logic s_valid_fc;
  logic s_valid_cl;
  logic s_valid_pr;

  logic [2:0] r_ls_sync;
  logic       s_ls_rise;

  logic [APB_EVNT_NUM-1:0] r_apb_events;

  assign fc_events_o = per_events_i[FC_EVENT_POS+1:FC_EVENT_POS];

  assign s_apb_addr = PADDR[7:2];

  assign s_ls_rise = ~r_ls_sync[2] & r_ls_sync[1];

  assign err_event_o = |(s_err);

  assign s_fc_mask = r_fc_mask[EVNT_NUM-1:0];
  assign s_cl_mask = r_cl_mask[EVNT_NUM-1:0];
  assign s_pr_mask = r_pr_mask[EVNT_NUM-1:0];

  assign fc_event_data_o = s_event_data;
  assign cl_event_data_o = s_event_data;
  assign pr_event_data_o = s_event_data;

  assign fc_event_valid_o = s_valid_fc;
  assign cl_event_valid_o = s_valid_cl;
  assign pr_event_valid_o = s_valid_pr;

  assign s_valid_fc = |(s_grant & ~s_fc_mask);
  assign s_valid_cl = |(s_grant & ~s_cl_mask);
  assign s_valid_pr = |(s_grant & ~s_pr_mask);

  assign s_ready_fc = s_valid_fc ? fc_event_ready_i : 1'b1;
  assign s_ready_cl = s_valid_cl ? cl_event_ready_i : 1'b1;
  assign s_ready_pr = s_valid_pr ? pr_event_ready_i : 1'b1;

  assign s_event_ready = s_ready_fc & s_ready_cl & s_ready_pr;

  assign s_events = {s_ls_rise,r_apb_events,per_events_i};

  assign s_ack = s_grant & {EVNT_NUM{s_event_ready}};

  assign timer_event_lo_o = s_events[r_timer_sel_lo];
  assign timer_event_hi_o = s_events[r_timer_sel_hi];

  generate
    for (j=0;j<EVNT_NUM;j++) begin
      soc_event_queue u_soc_event_queue (
        .clk_i       ( HCLK        ),
        .rstn_i      ( HRESETn     ),
        .event_i     ( s_events[j] ),
        .err_o       ( s_err[j]    ),
        .event_o     ( s_req[j]    ),
        .event_ack_i ( s_ack[j]    )
      );
    end
  endgenerate

  soc_event_arbiter #(.EVNT_NUM(EVNT_NUM)) u_arbiter (
    .clk_i       ( HCLK          ),
    .rstn_i      ( HRESETn       ),
    .req_i       ( s_req         ),
    .grant_o     ( s_grant       ),
    .grant_ack_i ( s_event_ready ),
    .anyGrant_o  ( s_event_valid )
  );

  always_comb begin : proc_data_o
    s_event_data = 'h0;
    for (int i=0;i<EVNT_NUM;i++)
      if(s_grant[i])
        s_event_data = i;
  end

  always @ (posedge HCLK or negedge HRESETn)
  begin
    if(~HRESETn)
      r_ls_sync <= 'h0;
    else
      r_ls_sync <= {r_ls_sync[1:0],low_speed_clk_i};
  end

  always @ (posedge HCLK or negedge HRESETn)
  begin
    if(~HRESETn) begin
      r_apb_events      =  'h0;
      r_fc_mask        <=  {256{1'b1}};
      r_cl_mask        <=  {256{1'b1}};
      r_pr_mask        <=  {256{1'b1}};
      r_timer_sel_lo   <=  'h0;
      r_timer_sel_hi   <=  'h0;
      r_err             =  'h0;
    end
    else begin
      for (int i=0;i<EVNT_NUM;i++)
        if(s_err[i])
          r_err[i] = 1'b1;
      r_apb_events     =  'h0;
      if (PSEL && PENABLE && PWRITE) begin
        case (s_apb_addr)
          `REG_EVENT:
          begin
            r_apb_events   = PWDATA[APB_EVNT_NUM-1:0];
          end
          `REG_FC_MASK_0:
          begin
              r_fc_mask[31:0]  <= PWDATA;
          end
          `REG_FC_MASK_1:
          begin
              r_fc_mask[63:32]  <= PWDATA;
          end
          `REG_FC_MASK_2:
          begin
              r_fc_mask[95:64]  <= PWDATA;
          end
          `REG_FC_MASK_3:
          begin
              r_fc_mask[127:96]  <= PWDATA;
          end
          `REG_FC_MASK_4:
          begin
              r_fc_mask[159:128]  <= PWDATA;
          end
          `REG_FC_MASK_5:
          begin
              r_fc_mask[191:160]  <= PWDATA;
          end
          `REG_FC_MASK_6:
          begin
              r_fc_mask[223:192]  <= PWDATA;
          end
          `REG_FC_MASK_7:
          begin
              r_fc_mask[255:224]  <= PWDATA;
          end
          `REG_CL_MASK_0:
          begin
              r_cl_mask[31:0]  <= PWDATA;
          end
          `REG_CL_MASK_1:
          begin
              r_cl_mask[63:32]  <= PWDATA;
          end
          `REG_CL_MASK_2:
          begin
              r_cl_mask[95:64]  <= PWDATA;
          end
          `REG_CL_MASK_3:
          begin
              r_cl_mask[127:96]  <= PWDATA;
          end
          `REG_CL_MASK_4:
          begin
              r_cl_mask[159:128]  <= PWDATA;
          end
          `REG_CL_MASK_5:
          begin
              r_cl_mask[191:160]  <= PWDATA;
          end
          `REG_CL_MASK_6:
          begin
              r_cl_mask[223:192]  <= PWDATA;
          end
          `REG_CL_MASK_7:
          begin
              r_cl_mask[255:224]  <= PWDATA;
          end
          `REG_PR_MASK_0:
          begin
              r_pr_mask[31:0]  <= PWDATA;
          end
          `REG_PR_MASK_1:
          begin
              r_pr_mask[63:32]  <= PWDATA;
          end
          `REG_PR_MASK_2:
          begin
              r_pr_mask[95:64]  <= PWDATA;
          end
          `REG_PR_MASK_3:
          begin
              r_pr_mask[127:96]  <= PWDATA;
          end
          `REG_PR_MASK_4:
          begin
              r_pr_mask[159:128]  <= PWDATA;
          end
          `REG_PR_MASK_5:
          begin
              r_pr_mask[191:160]  <= PWDATA;
          end
          `REG_PR_MASK_6:
          begin
              r_pr_mask[223:192]  <= PWDATA;
          end
          `REG_PR_MASK_7:
          begin
              r_pr_mask[255:224]  <= PWDATA;
          end
          `REG_TIMER1_SEL_LO:
          begin
            r_timer_sel_lo      <= PWDATA[7:0];
          end
          `REG_TIMER1_SEL_HI:
          begin
            r_timer_sel_hi      <= PWDATA[7:0];
          end
        endcase // s_apb_addr
      end else if (PSEL && PENABLE && ~PWRITE) begin
        case (s_apb_addr)
          `REG_ERR_0:
              r_err[31:0]  = 'h0;
          `REG_ERR_1:
              r_err[63:32] = 'h0;
          `REG_ERR_2:
              r_err[95:64] = 'h0;
          `REG_ERR_3:
              r_err[127:96] = 'h0;
          `REG_ERR_4:
              r_err[159:128] = 'h0;
          `REG_ERR_5:
              r_err[191:160] = 'h0;
          `REG_ERR_6:
              r_err[223:192] = 'h0;
          `REG_ERR_7:
              r_err[255:224] = 'h0;
        endcase // s_apb_addr
      end
    end
  end //always

  always_comb
  begin
    PRDATA = 'h0;
    case (s_apb_addr)
      `REG_FC_MASK_0:
          PRDATA = r_fc_mask[31:0];
      `REG_FC_MASK_1:
          PRDATA = r_fc_mask[63:32];
      `REG_FC_MASK_2:
          PRDATA = r_fc_mask[95:64];
      `REG_FC_MASK_3:
          PRDATA = r_fc_mask[127:96];
      `REG_FC_MASK_4:
          PRDATA = r_fc_mask[159:128];
      `REG_FC_MASK_5:
          PRDATA = r_fc_mask[191:160];
      `REG_FC_MASK_6:
          PRDATA = r_fc_mask[223:192];
      `REG_FC_MASK_7:
          PRDATA = r_fc_mask[255:224];
      `REG_CL_MASK_0:
          PRDATA = r_cl_mask[31:0];
      `REG_CL_MASK_1:
          PRDATA = r_cl_mask[63:32];
      `REG_CL_MASK_2:
          PRDATA = r_cl_mask[95:64];
      `REG_CL_MASK_3:
          PRDATA = r_cl_mask[127:96];
      `REG_CL_MASK_4:
          PRDATA = r_cl_mask[159:128];
      `REG_CL_MASK_5:
          PRDATA = r_cl_mask[191:160];
      `REG_CL_MASK_6:
          PRDATA = r_cl_mask[223:192];
      `REG_CL_MASK_7:
          PRDATA = r_cl_mask[255:224];
      `REG_PR_MASK_0:
          PRDATA = r_pr_mask[31:0];
      `REG_PR_MASK_1:
          PRDATA = r_pr_mask[63:32];
      `REG_PR_MASK_2:
          PRDATA = r_pr_mask[95:64];
      `REG_PR_MASK_3:
          PRDATA = r_pr_mask[127:96];
      `REG_PR_MASK_4:
          PRDATA = r_pr_mask[159:128];
      `REG_PR_MASK_5:
          PRDATA = r_pr_mask[191:160];
      `REG_PR_MASK_6:
          PRDATA = r_pr_mask[223:192];
      `REG_PR_MASK_7:
          PRDATA = r_pr_mask[255:224];
      `REG_ERR_0:
          PRDATA = r_err[31:0];
      `REG_ERR_1:
          PRDATA = r_err[63:32];
      `REG_ERR_2:
          PRDATA = r_err[95:64];
      `REG_ERR_3:
          PRDATA = r_err[127:96];
      `REG_ERR_4:
          PRDATA = r_err[159:128];
      `REG_ERR_5:
          PRDATA = r_err[191:160];
      `REG_ERR_6:
          PRDATA = r_err[223:192];
      `REG_ERR_7:
          PRDATA = r_err[255:224];
      `REG_TIMER1_SEL_LO:
        PRDATA = {24'h0,r_timer_sel_lo};
      `REG_TIMER1_SEL_HI:
        PRDATA = {24'h0,r_timer_sel_hi};
      default:
        PRDATA = 'h0;
    endcase
  end

  assign PREADY  = 1'b1;
  assign PSLVERR = 1'b0;

endmodule // soc_event_generator
